<!DOCTYPE html>
<html>

  <head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>
    编译器开发从零单排(3)
    </title>
  <link type="application/atom+xml" rel="alternate" href="/feed.xml" title="虚实" />
  
  <style>
  table{
    border-left:1px solid #000000;border-top:1px solid #000000;
    width: 100%;
    word-wrap:break-word; word-break:break-all;
  }
  table th{
  text-align:center;
  }
  table th,td{
    border-right:1px solid #000000;border-bottom:1px solid #000000;
  }
  </style>

  <meta name="description" content="NaiveCompiler 前端设计">
  <link rel="stylesheet" href="/css/main.css">
  <link rel="stylesheet" href="/css/toc.css">
  <link rel="canonical" href="/2019/05/27/%E7%BC%96%E8%AF%91%E5%99%A8%E5%BC%80%E5%8F%91%E4%BB%8E%E9%9B%B6%E5%8D%95%E6%8E%92%283%29/">
  <link rel="alternate" type="application/rss+xml" title="虚实" href="/feed.xml" />
  <link rel="stylesheet" href="/css/highlight.min.css">
  <link href="//cdn.staticfile.org/font-awesome/3.2.1/css/font-awesome.min.css" rel="stylesheet"media="all">
</head>


  <body>

    <header class="site-header">

  <div class="wrapper">
    <div>
      <a class="site-title" href="/">虚实</a>
      <div class="site-pages">
		<a class="site-page" href="/archive/">归档</a>
		<a class="site-page" href="/categories/">分类</a>
		<a class="site-page" href="/about/">关于</a>
		<a class="site-page" href="/friend-links/">友情链接</a>
        <a class="site-page" href="/recommends/">推荐</a>
      </div>
      <p class="site-sub-title">记录下折腾和学习的过程</p>
    </div>

  </div>

</header>


    <div class="page-content">
      <div class="wrapper">
        <div class="post">

  <header class="post-header">
    <h1 class="post-title">编译器开发从零单排(3)</h1>
    <p class="post-meta">May 27, 2019 • admin</p>

	<p class="post-meta"> categories :   <a href="/categories/#compiler"> compiler </a>  </p>
    <div id="show_qrcode">
        <a>扫描二维码</a>
        <div id="qrcode" style="display:none;position:absolute;z-index:1"></div>
    </div>
  </header>
  <div class="nav">
      <div id="toc" class="toc"></div>
  </div>
  <article class="post-content">
    <h1 id="naivecompiler-前端设计">NaiveCompiler 前端设计</h1>

<h2 id="语义分析-semantic-analysis">语义分析 (semantic analysis)</h2>

<p>编译器在完成语法分析之后的步骤为语义分析，该步骤会添加语义信息到 Parse Tree 中，并构建符号表(Symbol Table)。</p>

<p>在这个过程中，会同时进行语义检测，例如：类型检查（type checking），以及定义赋值检查（definite assignment analysis）等。</p>

<p>语义分析通常是基于 Parse Tree 或者 AST 进行。 NaiveCompiler 的语义分析实现于 <code class="language-plaintext highlighter-rouge">analysis_handler.py</code> 中。
<code class="language-plaintext highlighter-rouge">analysis_handler.py</code> 中的类继承 <code class="language-plaintext highlighter-rouge">visitor.py</code> 中实现的 visitor 访问模式类 <code class="language-plaintext highlighter-rouge">NodeVisitor</code>，实现对 AST 的访问和分析。</p>

<p>Clang 中的语义分析定义于 <a href="https://code.woboq.org/llvm/clang/lib/Sema/SemaChecking.cpp.html"><code class="language-plaintext highlighter-rouge">SemaChecking.cpp</code></a> 中。同样是通过 Visitor 模式实现。</p>

<h3 id="visitor-模式及其实现">Visitor 模式及其实现</h3>

<p>Visitor 模式是《设计模式》(GoF design patterns)中的 23 个设计模式之一，可以实现算法和实际对象结构的分离，可以在不修改对象结构的情况下给对象添加新的方法。</p>

<p>在 C++ 中可以利用函数重载，double dispatch 来实现 visitor 模式，不需要修改已定义好的 class 源码。而 Python 中则更为简单，通过反射获取类名即可实现。</p>

<p>Visitor 模式还有一个好处，即遍历 AST 时，所有的节点都是继承自 ASTNode，所以可以方便的递归遍历整颗树，并只对感兴趣的节点做处理。</p>

<p>在 NaiveCompiler 的前端中， visitor 都继承自 <code class="language-plaintext highlighter-rouge">visitor.py</code> 中的 <code class="language-plaintext highlighter-rouge">NodeVisitor</code>。该源码修改自 CPython 自身的 <a href="https://github.com/python/cpython/blob/2.7/Lib/ast.py#L217"><code class="language-plaintext highlighter-rouge">NodeVisitor</code></a>(定义于/Lib/ast.py)。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class NodeVisitor(object):
    def visit(self, node):
        method = 'visit_' + node.__class__.__name__
        visitor = getattr(self, method, self.generic_visit)
        return visitor(node)

    def generic_visit(self, node):
        for c in node.children():
            self.visit(c)
</code></pre></div></div>

<p>要实现特定的功能只需要继承该类，以 <code class="language-plaintext highlighter-rouge">visit_XXX</code> 的方式定义想要处理的节点，没有定义对应函数的节点会调用 <code class="language-plaintext highlighter-rouge">generic_visit</code> 函数，自动访问其子节点。</p>

<p>例如做 定义赋值检查（definite assignment analysis）时，
<code class="language-plaintext highlighter-rouge">AnalysisVisitor</code> 继承 <code class="language-plaintext highlighter-rouge">NodeVisitor</code> ，并在访问到函数定义时切换到针对 <code class="language-plaintext highlighter-rouge">FuncDef</code> 的类。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class FuncHelper(NodeVisitor):
    def __init__(self):
        self.scope = Scope()
        
    def _has_error(self):
        return self.scope.has_error
    
    def visit_TypeDecl(self, node):
        # print 'TypeDecl', node.__class__.__name__
        self.scope.define_symbol(node._id.name, node._type)

    def visit_DeclStmt(self, node):
        # self.generic_visit(node.decl)
        self.visit_TypeDecl(node.decl)
        
    def visit_ContinueStmt(self, node):
        logging.error("continue outside loop!")
        self.scope.has_error = True

    def visit_BreakStmt(self, node):
        logging.error("break outside loop!")
        self.scope.has_error = True
        
    def visit_Assignment(self, node):
        if type(node.cast_expr) is VariableSymbol:
            self.scope.resolve_symbol(node.cast_expr.name)
        #helper = AssignmentExprHelper(self.scope)
        #helper.visit(node)
</code></pre></div></div>

<p>可以看到，在访问到声明和赋值语句时，会回调 <code class="language-plaintext highlighter-rouge">visit_TypeDecl</code> 和 <code class="language-plaintext highlighter-rouge">visit_Assignment</code>。</p>

<p>其中 <code class="language-plaintext highlighter-rouge">visit_TypeDecl</code> 会在符号表中注册该变量，而 <code class="language-plaintext highlighter-rouge">visit_Assignment</code> 会检测该变量是否存在于符号表中(同时也可以检测变量的类型和赋值的对象是否相同)。</p>

<p>而 <code class="language-plaintext highlighter-rouge">visit_BreakStmt</code> 和 <code class="language-plaintext highlighter-rouge">visit_ContinueStmt</code> 则会检查是否有循环外的 break 和 continue 语句。因为循环语句会触发 <code class="language-plaintext highlighter-rouge">visit_WhileStmt</code> ，进入 LoopHelper 对象进行下一步处理，在没有进去 LoopHelper 访问到的 break 和 continue 即为循环外定义的非法语句。</p>

<h2 id="中间代码生成">中间代码生成</h2>

<p>通常的代码生成过程为：源代码解析为的 AST 或者三地址代码，生成高级的中间代码(HIR)，然后可能会生成中级的中间代码(MIR)或低级的中间代码(LIR)，中间代码一般是机器无关的代码，最后编译器在中间代码的基础上生成机器相关的代码（例如 x86 汇编代码）。MIR 基本上适合大多是优化，HIR 则用于依赖分析等场景，LIR 用于明确指明寄存器和地址等的优化，参考《高级编译器的设计与实现》(鲸书)第四章《中间表示》。</p>

<p><img src="/imgs/CompilerGenerationFramework.png" alt="Compiler Generation Framework" /></p>

<p>最常见的 HIR 即为抽象语法树(AST)，而 MIR 需要表示源代码的变量、临时变量、寄存器，能够把控制流归约为简单的有条件跳转和无条件跳转、函数调用(call)、和返回(ret)，还需要用明显的操作来支持块结构(BasicBlock)和过程(Function)。</p>

<p>BasicBlock 是 CFG 的基本组成部分，有一系列除了头和尾没有分支的代码序列，一个 BasicBlock 中的代码会按顺序依次执行。</p>

<p>一个 BasicBlock 只能有一个入口(entry point)，意味着 BasicBlock 中的代码不能是某个跳转指令的目标。
一个 BasicBlock 只能有一个出口(exit point)，代表只有 BasicBlock 中的最后一条指令才允许跳转到另外的 BasicBlock 中开始执行。</p>

<p>BasicBlock(简称 BB 块)之间的关系为，BB块 结束后跳转的目标称作 successors，一个 BB 块可能有多个 successors （条件跳转）。 跳转到先有 BB 块的称作 predecessors，一个 BB 块也可能有多个 predecessors。</p>

<p>下面将以 clang 和 gcc 为例，通过下面的 testif.c 来说明常见的中间代码形式和生成过程。</p>

<p>testif.c</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int main()
{
  int a = 1;
  if (a &gt; 0) {
      return 1;
  }
  return 0;
}
</code></pre></div></div>

<h3 id="clang-中的中间代码生成">Clang 中的中间代码生成</h3>

<p>Clang 中会先将 AST 划分为 CFG(<a href="https://en.wikipedia.org/wiki/Control-flow_graph">Control-flow_graph</a>), 用于进一步的分析和平台无关的优化。然后再以类似划分 CFG 的逻辑划分 Basic Blocks，并生成三地址代码。</p>

<p>Clang 中会生成 LLVM 的 IR，再调用 LLVM 生成目标机器码，使用 <code class="language-plaintext highlighter-rouge">clang  -S -emit-llvm $1</code> 可以 dump 出 Clang 生成的 LLVM IR。以下面的测试代码为例:</p>

<p>生成的 testif.ll 代码如下：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>define i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store i32 1, i32* %2, align 4
  %3 = load i32, i32* %2, align 4
  %4 = icmp sgt i32 %3, 0
  br i1 %4, label %5, label %6

; &lt;label&gt;:5:                                      ; preds = %0
  store i32 1, i32* %1, align 4
  br label %7

; &lt;label&gt;:6:                                      ; preds = %0
  store i32 0, i32* %1, align 4
  br label %7

; &lt;label&gt;:7:                                      ; preds = %6, %5
  %8 = load i32, i32* %1, align 4
  ret i32 %8
}
</code></pre></div></div>

<p>可以看到 C 代码中的 if 语句编译为了 icmp 和 br，然后 if 和 else 分支分别对应 label 5 和 label6。
而 label 7 为 if 后续的语句，可以看到 label 7 的注释中标注有 preds = %6, %5，其代表着 label 7 的前置 label 为 5 和 6，这代表了 BasicBlock 间的关系。除了第一个 BasicBlock，如果某个 BasicBlock 没有前置的 BasicBlock，则该分支为无效的分支。下面我们将一起来看一下 BasicBlock 的具体概念。</p>

<h3 id="gcc-中的中间代码生成">GCC 中的中间代码生成</h3>

<p>以上面的 <code class="language-plaintext highlighter-rouge">testif.c</code> 为例，使用 <code class="language-plaintext highlighter-rouge">gcc -fdump-tree-all testif.c</code> 可以 dump 出 gcc 中的中间语言 gimple(<code class="language-plaintext highlighter-rouge">.gimple</code> 文件，为高级的中间表达式) 以及 cfg(<code class="language-plaintext highlighter-rouge">.cfg</code> 由高级的gimple生成低级的中间表达式)。</p>

<p>上面的 <code class="language-plaintext highlighter-rouge">testif.c</code> 生成的 cfg 如下</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;;  nodes: 0 1 2 3 4 5
;; 2 succs { 3 4 }
;; 3 succs { 5 }
;; 4 succs { 5 }
;; 5 succs { 1 }
main ()
{
  int a;
  int D.1798;

  &lt;bb 2&gt; [0.00%]:
  a = 1;
  if (a &gt; 0)
    goto &lt;bb 3&gt;; [0.00%]
  else
    goto &lt;bb 4&gt;; [0.00%]

  &lt;bb 3&gt; [0.00%]:
  D.1798 = 1;
  goto &lt;bb 5&gt; (&lt;L2&gt;); [0.00%]

  &lt;bb 4&gt; [0.00%]:
  D.1798 = 0;

&lt;L2&gt; [0.00%]:
  return D.1798;

}
</code></pre></div></div>

<p>关于 gcc 的 gimple 中间表达式可以参考 https://www.cse.iitb.ac.in/grc/gcc-workshop-09/downloads/gccw09-gimple.pdf，其主要关注下面三个方面：</p>
<ul>
  <li>简化控制流：将代码转换为简单的一系列语句(statment) 和 跳转。</li>
  <li>简化表达式：例如 <code class="language-plaintext highlighter-rouge">-=</code>、<code class="language-plaintext highlighter-rouge">+=</code></li>
  <li>简化作用域：将变量包括临时变量移动到作用域开始处</li>
</ul>

<h3 id="naivecompiler-中的中间代码生成">NaiveCompiler 中的中间代码生成</h3>

<p>NaiveCompiler 中构建 CFG 后没有进行接下来的分析操作，直接开始做代码生成。</p>

<p>在做完语义分析生成 AST 之后 NaiveCompiler 将循环和条件语句改写为中间代码，分成多个 Baisc Block，并序列化中间代码的语法树，提供给后端 C++ 引擎以便调用 LLVM 来生成 IR 最后编译为目标文件。</p>

<p>示例的 <code class="language-plaintext highlighter-rouge">testif.c</code> 对应的语法树如下:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python compiler.py --show-ast tests/testif.c 
----------- show ast ------------
AST: 
  FuncDef: return_type=int, storage=extern
    MethodSymbol: name=main
    DeclarationList: 
    StmtList: 
      DeclStmt: 
        TypeDecl: _type=int, storage=auto
          VariableSymbol: name=a
          Const: _type=int, val=1
      IfStmt: 
        BinaryOp: op=&gt;
          VariableSymbol: name=a
          Const: _type=int, val=0
        StmtList: 
          ReturnStmt: 
            Const: _type=int, val=1
      ReturnStmt: 
        Const: _type=int, val=0
</code></pre></div></div>

<p>因为后端使用 LLVM 来做代码生成，NavieCompiler 前端中生成的中间代码需要配合 LLVM，考虑 LLVM 代码生成的 API 中，是以 BasicBlock 为基本模块的，并且并无 if 等条件语句，和 for、while 等循环语句，而是类似汇编中的 cmp 语句，和跳转语句 br。（区别为 x86 汇编中 cmp 只有一个，cmp 后会设置条件寄存器，然后有多种 jmp 语句来根据条件寄存器的标志位进行跳转。而 LLVM 的 IR 中 cmp 可以传入比较参数，而跳转均为 br，br 可以带条件结果或者不带条件直接跳转）。</p>

<p>而 LLVM 中的代码(IR)都属于某个函数，而其基本的组成模块为 BasicBlock，每个 BasicBlock 都有一个编号，可以供其他 BasicBlock 跳转时使用。</p>

<p>NaiveCompiler 中 Basic Block 的生成逻辑实现在 <code class="language-plaintext highlighter-rouge">codegen.py</code> 中。
以上面的 <code class="language-plaintext highlighter-rouge">testif.c</code> 为例，使用 <code class="language-plaintext highlighter-rouge">python compiler --show-cfg</code> 得到 CFG 如下:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Func  main
4 Entry
Succs: [3]

3
DeclStmt:
  TypeDecl: _type=int, storage=auto
    VariableSymbol: name=a
    Const: _type=int, val=1
CMPJMP: id1=2, id2=1
  BinaryOp: op=&gt;
    VariableSymbol: name=a
    Const: _type=int, val=0
Succs: [2, 1]

2
ReturnStmt:
  Const: _type=int, val=1
Succs: [0]

1
ReturnStmt:
  Const: _type=int, val=0
Succs: [0]

0 Exit
Succs: []
</code></pre></div></div>

<h4 id="basicblock">BasicBlock</h4>

<p><code class="language-plaintext highlighter-rouge">codegen.py</code> 中，首先定义了 BasicBlock 的基本结构，按照上面的定义，一个基本块需要有一个独立的 ID，以及跳转到该基本块的前置基本块(preds)和该基本块的后续基本块(successors)，还需要 LoopTarget 和 Terminator 辅助循环语句的生成，当然，语句列表(stmts)也不能少。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class BasicBlock(object):
    """ Hold a BasicBlock, To Replace AST Label"""
    BlockKind = ["Reachable", "Unreachable", "Unknown"] # 该 BasicBlock 是否可达
    def __init__(self):
        self.Label = None # Label
        self.Label_id = -1 # Label id
        self.block_id = id_generator.next() # block id
        self.block_name = ''
        self.stmts = [] # StmtList
        self.preds = []
        self.successors = []
        self.Terminator = None
        self.LoopTarget = None
</code></pre></div></div>

<h4 id="codegenerator">CodeGenerator</h4>

<p>代码生成时，以每个 Function 作为一个模块，每个模块由多个基本块组成，其中有一个默认得 Entry 和 一个 Exit 块。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def build_basic_blocks(ast):
    basic_blocks = {}
    for c in ast.children():
        if c.__class__ is FuncDef:
            bb = CodeGenModule.build_basic_blocks(c.body, c)
            basic_blocks[c] = bb
    return basic_blocks
</code></pre></div></div>

<p>定义 <code class="language-plaintext highlighter-rouge">CodeGenerator</code> 用于代码生成，其中 cgm 存放当前模块的所有基本块，该类中定义的 <code class="language-plaintext highlighter-rouge">current_block</code> 和 <code class="language-plaintext highlighter-rouge">current_successor</code> 负责确定当前处理的基本块和当前块的后续基本块。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class CodeGenerator(object):
    """ 至低向上构建 CFG，向构建继承的 block 再构建 上一层的 block"""
    Terminator_STMTS = ["BreakStmt", "ContinueStmt", "ReturnStmt"]
    def __init__(self):
        self.cgm = CodeGenModule()
        self.current_block = None
        self.current_successor = None
        self.break_jumptarget = None
        self.continue_jumptarget = None
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">CodeGenerator</code> 反向遍历 FuncDef 节点的 StmtList 子节点，构建 CFG。其代码参考了 Clang 的 <code class="language-plaintext highlighter-rouge">CFG.cpp</code>。</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># CFG.cpp::1124
# https://code.woboq.org/llvm/clang/lib/Analysis/CFG.cpp.html#_ZN12_GLOBAL__N_110CFGBuilder11createBlockEb
def build_basic_blocks(self, node, parent):
    """ 将 StmtList 转换为 CFG，从尾向头遍历子节点"""
    assert node.__class__.__name__ == 'StmtList'
    self.current_successor = self.create_block() # exit Block
    self.current_block = None
    
    block = self.visit(node)
    
    if block is not None:
        self.current_successor = block

    return self.cgm
</code></pre></div></div>

<p>因为从尾向头遍历，所以先创建第一个默认的 Exit Block，并设置为 <code class="language-plaintext highlighter-rouge">current_successor</code>，这样遍历到倒数第一个语句时，将会自动创建一个基本块并将其 Exit Block 添加到其 successors 中。</p>

<p>因为 FuncDef 的第一个 node 为 StmtList，默认的 visit 函数会调用 <code class="language-plaintext highlighter-rouge">visit_StmtList</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def visit(self, node):
    """ Visit a Stmt.
    """
    node_class =  node.__class__.__name__
    # print node_class
    if issubclass(node.__class__, Statement) or node.__class__ is StmtList:
        method = 'visit_' + node_class
        visitor = getattr(self, method)
    else:
        logger.warning("Unsupported Stmt: {0}".format(node_class))
        sys.exit(1)
    return visitor(node)
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># https://code.woboq.org/llvm/clang/lib/Analysis/CFG.cpp.html VisitCompoundStmt
def visit_StmtList(self, node):
    last_block = self.current_block
    for c in node.children()[::-1]:
        tmp = self.visit(c)
        if tmp:
            last_block = tmp
    return last_block
</code></pre></div></div>

<p>可以看到 <code class="language-plaintext highlighter-rouge">visit_StmtList</code> 会反向遍历其子节点，即每个 Stmt，并返回最后一个不为空的基本块，如果没有发生运行错误，该块应为 Entry Block。</p>

<h3 id="refs">Refs</h3>

<p>https://en.wikipedia.org/wiki/Basic_block
https://www.cse.iitb.ac.in/grc/gcc-workshop-09/downloads/gccw09-gimple.pdf
http://amnoid.de/tmp/clangtut/tut.html</p>

  </article>

</div>
<div id="disqus_thread"></div>
<script type="text/javascript">
    /* * * CONFIGURATION VARIABLES * * */
    var disqus_shortname = 'yoursoulismine';
    
    /* * * DON'T EDIT BELOW THIS LINE * * */
    (function() {
        var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
        dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
    })();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
<script type="text/javascript">
    /* * * CONFIGURATION VARIABLES * * */
    var disqus_shortname = 'yoursoulismine';
    
    /* * * DON'T EDIT BELOW THIS LINE * * */
    (function () {
        var s = document.createElement('script'); s.async = true;
        s.type = 'text/javascript';
        s.src = '//' + disqus_shortname + '.disqus.com/count.js';
        (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
    }());
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://flowchart.js.org/flowchart-latest.js"></script>
<script src="/js/highlight.min.js"></script>
<script src="/js/toc.js"></script>
<script src="/js/qrcode.min.js"></script>
<script>
if (!String.prototype.format) {
    String.prototype.format = function() {
        var args = arguments;
        return this.replace(/{(\d+)}/g, function(match, number) { 
            return typeof args[number] != 'undefined'
                ? args[number]
                : match
            ;
        });
    };
}

$('pre code').each(function(i, block) {
    hljs.highlightBlock(block);
});

$('.language-flow').each(function(i, block) {
    console.log(block);
    code = $(this).text();
    diagram = flowchart.parse(code);
    canvas_id = 'flow'+ i;
    console.log(canvas_id);
    temp = "<div id='{0}'> </div>".format(canvas_id);
    console.log(temp);
    $(this).html(temp);
    diagram.drawSVG(canvas_id, {
        'x': 0,
        'y': 0,
        'line-width': 3,
        'line-length': 50,
        'text-margin': 10,
        'font-size': 14,
    });	
});

$('#toc').toc({
    noBackToTopLinks: true,
    listType: 'ul',
    title: 'TOC',
});

var url = location.href;
console.log(url);
var qrcode = new QRCode("qrcode", {
    text: url,
    width: 128,
    height: 128,
    colorDark : "#000000",
    colorLight : "#ffffff",
    correctLevel : QRCode.CorrectLevel.H
});

$(document).ready(function () {
    $('#show_qrcode').on('mouseenter', function () {
        $('#qrcode').show();
        $(this).css({
            "text-decoration": "underline"
        });
    }).on('mouseleave', function () {
        $('#qrcode').hide();
        $(this).css({
            "text-decoration": ''
        });
    });;
});
</script>

      </div>
    </div>

    <footer class="site-footer">

  <div class="wrapper">


    <div class="footer-col-wrapper">
      <div class="footer-col  footer-col-1">
        <ul class="contact-list">
	  <li>
          <li><a href="mailto:mithrilwoodrat@gmail.com">mithrilwoodrat@gmail.com</a></li>
        </ul>
      </div>

      <div class="footer-col  footer-col-2">
        <ul class="social-media-list">
          
          <li>
            <a href="https://github.com/Mithrilwoodrat">
              <span class="icon  icon--github">
                <svg viewBox="0 0 16 16">
                  <path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/>
                </svg>
              </span>
              <span class="username">Mithrilwoodrat</span> 
            </a>
          </li>
          
        </ul>
      </div>
  </div>
</footer>

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-66599686-1', 'auto');
  ga('send', 'pageview');

</script>
</body>
</html>
